#include <iostream>
#include "rwSocket.h"

using namespace std;

int rwSocket::nofSockets_= 0;

void rwSocket::Start() {
  if (!nofSockets_) {
    WSADATA info;
    if (WSAStartup(MAKEWORD(2,0), &info)) {
      throw "Could not start WSA";
    }
  }
  ++nofSockets_;
}

void rwSocket::End() {
  WSACleanup();
}

rwSocket::rwSocket() : s_(0) {
  Start();
  // UDP: use SOCK_DGRAM instead of SOCK_STREAM
  s_ = socket(AF_INET,SOCK_STREAM,0);

  if (s_ == INVALID_SOCKET) {
    throw "INVALID_SOCKET";
  }

  refCounter_ = new int(1);
}

rwSocket::rwSocket(SOCKET s) : s_(s) {
  Start();
  refCounter_ = new int(1);
};

rwSocket::~rwSocket() {
  if (! --(*refCounter_)) {
    Close();
    delete refCounter_;
  }

  --nofSockets_;
  if (!nofSockets_) End();
}

rwSocket::rwSocket(const rwSocket& o) {
  refCounter_=o.refCounter_;
  (*refCounter_)++;
  s_         =o.s_;

  nofSockets_++;
}

rwSocket& rwSocket::operator=(rwSocket& o) {
  (*o.refCounter_)++;

  refCounter_=o.refCounter_;
  s_         =o.s_;

  nofSockets_++;

  return *this;
}

void rwSocket::Close() {
  closesocket(s_);
}

std::string rwSocket::ReceiveBytes() {
  std::string ret;
  char buf[1024];

  while (1) {
    u_long arg = 0;
    if (ioctlsocket(s_, FIONREAD, &arg) != 0)
      break;

    if (arg == 0)
      break;

    if (arg > 1024) arg = 1024;

    int rv = recv (s_, buf, arg, 0);
    if (rv <= 0) break;

    std::string t;

    t.assign (buf, rv);
    ret += t;
  }

  return ret;
}

std::string rwSocket::ReceiveLine() {
  std::string ret;
  while (1) {
    char r;

    switch(recv(s_, &r, 1, 0)) {
      case 0: // not connected anymore;
              // ... but last line sent
              // might not end in \n,
              // so return ret anyway.
		cout << "User " << &s_ << " Connection Broken(0)" << endl;
		closesocket(s_);
		return "@@EXIT@@";
      case -1:
		cout << "User " << &s_ << " Connection Broken(-1)" << endl;
		return "@@EXIT@@";
//      if (errno == EAGAIN) {
//        return ret;
//      } else {
//      // not connected anymore
//      return "";
//      }
    }

	//this line can display receive char ASCII code.
	//printf("(%d)\n",r);

    if (r == 13 or r == 10)
	{
		/*
		if (ret != "")
		{
			std::cout << ret << std::endl;
		}
		*/
		return ret;
	}
    ret += r;
  }
}

void rwSocket::SendLine(std::string s) {
  s += "\r\n";
  send(s_,s.c_str(),s.length(),0);
}

void rwSocket::SendBytes(const std::string& s) {
  send(s_,s.c_str(),s.length(),0);
}

SocketServer::SocketServer(int port, int connections, TyperwSocket type) {
  sockaddr_in sa;

  memset(&sa, 0, sizeof(sa));

  sa.sin_family = PF_INET;
  sa.sin_port = htons(port);
  s_ = socket(AF_INET, SOCK_STREAM, 0);
  if (s_ == INVALID_SOCKET) {
    throw "INVALID_SOCKET";
  }

  if(type==NonBlockingSocket) {
    u_long arg = 1;
    ioctlsocket(s_, FIONBIO, &arg);
  }

  /* bind the socket to the internet address */
  if (bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR) {
    closesocket(s_);
    throw "INVALID_SOCKET";
  }

  listen(s_, connections);
}

rwSocket* SocketServer::Accept() {
  SOCKET new_sock = accept(s_, 0, 0);
  if (new_sock == INVALID_SOCKET) {
    int rc = WSAGetLastError();
    if(rc==WSAEWOULDBLOCK) {
      return 0; // non-blocking call, no request pending
    }
    else {
      throw "Invalid Socket";
    }
  }

  rwSocket* r = new rwSocket(new_sock);
  return r;
}

SocketClient::SocketClient(const std::string& host, int port) : rwSocket() {
  std::string error;

  hostent *he;
  if ((he = gethostbyname(host.c_str())) == 0) {
    error = strerror(errno);
    throw error;
  }

  sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr = *((in_addr *)he->h_addr);
  memset(&(addr.sin_zero), 0, 8);

  if (::connect(s_, (sockaddr *) &addr, sizeof(sockaddr))) {
    error = strerror(WSAGetLastError());
    throw error;
  }
}

SocketSelect::SocketSelect(rwSocket const * const s1, rwSocket const * const s2, TyperwSocket type) {
  FD_ZERO(&fds_);
  FD_SET(const_cast<rwSocket*>(s1)->s_,&fds_);
  if(s2) {
    FD_SET(const_cast<rwSocket*>(s2)->s_,&fds_);
  }

  TIMEVAL tval;
  tval.tv_sec  = 0;
  tval.tv_usec = 1;

  TIMEVAL *ptval;
  if(type==NonBlockingSocket) {
    ptval = &tval;
  }
  else {
    ptval = 0;
  }

  if (select (0, &fds_, (fd_set*) 0, (fd_set*) 0, ptval) == SOCKET_ERROR)
    throw "Error in select";
}

bool SocketSelect::Readable(rwSocket const* const s) {
  if (FD_ISSET(s->s_,&fds_)) return true;
  return false;
}
